#pragma once
#ifndef OBJECT_MANAGER_HPP
#define OBJECT_MANAGER_HPP
#include <string>
#include <unordered_map>
#include <random>
#include <mutex>
#include <memory>
#include <typeindex>
#include <functional>
namespace HandleSystem {
	using ObjectHandle = int;
	struct TypeErasedDeleter {
		using DeleteFunc = std::function<void(void*)>;
		DeleteFunc deleter;

		template<typename T>
		static TypeErasedDeleter Create() {
			return {
				[](void* ptr) {
					delete static_cast<std::shared_ptr<T>*>(ptr);
				}
			};
		}

		void operator()(void* ptr) const { deleter(ptr); }
	};
	struct HandleContext {
		void* object;
		std::string fullName;
		std::shared_ptr<int> handleCount;
		std::type_index typeIndex;
		TypeErasedDeleter deleter;
	};
	struct HandleManagerEnv {
		std::unordered_map<ObjectHandle, HandleContext*> handleToContextMap;
		std::unordered_map<std::string, HandleContext*> nameToContextMap;
		std::mutex mutex;
	};
	template <typename TObject>
	class Object {
	private:
		HandleManagerEnv* env;
		std::shared_ptr<TObject> object;
		std::shared_ptr<int> handleCount;
	public:
		Object(HandleManagerEnv* e, std::shared_ptr<TObject> o, std::shared_ptr<int> hc) : env(e), object(o), handleCount(hc){
		};
		Object() = delete;
		std::shared_ptr<TObject> SharedPtr() {
			std::lock_guard<std::mutex> lock(env->mutex);
			return object;
		}
		int HandleCount() {
			std::lock_guard<std::mutex> lock(env->mutex);
			return *handleCount;
		}
	};
	template <typename T>
	class IObjectFactory {
	public:
		virtual ~IObjectFactory() = default;
		virtual std::shared_ptr<T>* Create() = 0;
	};
	template <typename TObject>
	class DefaultObjectFactory : public IObjectFactory<TObject> {
	public:
		std::shared_ptr<TObject>* Create() override {
			return new std::shared_ptr<TObject>(new TObject);
		}
	};
	template <typename TObject, typename TFactory = DefaultObjectFactory<TObject>>
	class ObjectHandleManager {
		using ObjectPtr = std::shared_ptr<TObject>;
	private:
		HandleManagerEnv* env;
		std::random_device rd;
		std::mt19937 gen;
		std::uniform_int_distribution<int> dist;
		const std::type_index managedType;
		TFactory defaultFactory;
		ObjectHandle GenerateUniqueHandle() {
			ObjectHandle handle;
			do {
				handle = dist(gen);
			} while (env->handleToContextMap.find(handle) != env->handleToContextMap.end() || handle == 0);
			return handle;
		}
		bool IsValidName(const std::string& name) {
			return name.find(DELIMITER) == std::string::npos;
		}
		std::string CreateFullObjectName(const std::string& objectName, const std::string& typeSpace) {
			return objectName.empty() ?
				"ANON" + std::string(1, DELIMITER) + typeSpace + std::string(1, DELIMITER) + std::to_string(dist(gen)) :
				objectName + std::string(1, DELIMITER) + typeSpace + std::string(1, DELIMITER);
		}
		void RegisterHandleToContext(ObjectHandle handle, HandleContext* context) {
			env->handleToContextMap[handle] = context;
			(*context->handleCount)++;
		}
		HandleContext* CreateObjectContext(const std::string& fullName, TFactory& factory) {
			ObjectPtr* object = factory.Create();
			return new HandleContext{ static_cast<void*>(object),fullName,std::shared_ptr<int>(new int(0)),managedType,TypeErasedDeleter::Create<TObject>() };
		}
		bool CheckTypeSafety(const HandleContext* ctx) {
			return ctx->typeIndex == managedType;
		}
	public:
		static constexpr char DELIMITER = '#';
		ObjectHandleManager(HandleManagerEnv* e) : env(e), gen(rd()), dist(1000, 0x7FFFFFFF), managedType(typeid(TObject)) {
		}
		ObjectHandleManager() = delete;
		// ֹ
		ObjectHandleManager(const ObjectHandleManager&) = delete;
		ObjectHandleManager& operator=(const ObjectHandleManager&) = delete;

		ObjectHandle CreateObject(const std::string& objectName, const std::string& typeSpace, TFactory& factory) {
			std::lock_guard<std::mutex> lock(env->mutex);
			if (!IsValidName(objectName) || !IsValidName(typeSpace)) {
				return 0;
			}
			std::string fullName;
			ObjectHandle handle = GenerateUniqueHandle();
			std::unordered_map<std::string, HandleContext*>::iterator it;
			do {
				fullName = CreateFullObjectName(objectName, typeSpace);
				it = env->nameToContextMap.find(fullName);
			} while (objectName.empty() && it != env->nameToContextMap.end());
			HandleContext* ctx = nullptr;
			if (it != env->nameToContextMap.end()) {
				ctx = it->second;
				if (!CheckTypeSafety(ctx)) {
					return 0;
				}
			}
			else {
				ctx = CreateObjectContext(fullName, factory);
				env->nameToContextMap[fullName] = ctx;
			}
			RegisterHandleToContext(handle, ctx);
			return handle;
		};
		ObjectHandle CreateObject(const std::string& objectName, const std::string& typeSpace) {
			return CreateObject(objectName, typeSpace, defaultFactory);
		}
		ObjectHandle OpenObject(const std::string& objectName, const std::string& typeSpace) {
			std::lock_guard<std::mutex> lock(env->mutex);
			if (!IsValidName(objectName) || !IsValidName(typeSpace) || objectName.empty()) {
				return 0;
			}
			std::string fullName = objectName + std::string(1, DELIMITER) + typeSpace + std::string(1, DELIMITER);
			auto it = env->nameToContextMap.find(fullName);
			if (it != env->nameToContextMap.end()) {
				HandleContext* ctx = it->second;
				if (!CheckTypeSafety(ctx)) {
					return 0;
				}
				ObjectHandle handle = GenerateUniqueHandle();
				RegisterHandleToContext(handle, ctx);
				return handle;
			}
			return 0;
		};
		bool CloseHandle(ObjectHandle handle) {
			std::lock_guard<std::mutex> lock(env->mutex);
			auto handleIt = env->handleToContextMap.find(handle);
			if (handleIt == env->handleToContextMap.end()) {
				return false;
			}
			HandleContext* ctx = handleIt->second;
			if (!CheckTypeSafety(ctx)) {
				return false;
			}
			env->handleToContextMap.erase(handleIt);
			if (--*ctx->handleCount == 0) {
				auto nameIt = env->nameToContextMap.find(ctx->fullName);
				env->nameToContextMap.erase(nameIt);
				ctx->deleter(ctx->object);
				delete ctx;
			}
			return true;
		}
		Object<TObject> GetObject(ObjectHandle handle) {
			std::lock_guard<std::mutex> lock(env->mutex);
			auto it = env->handleToContextMap.find(handle);
			if (it == env->handleToContextMap.end()) {
				return { env,ObjectPtr(nullptr),std::shared_ptr<int>(nullptr) };
			}
			HandleContext* ctx = it->second;
			if (!CheckTypeSafety(ctx)) {
				return { env,ObjectPtr(nullptr),std::shared_ptr<int>(nullptr) };
			}
			return { env,*static_cast<ObjectPtr*>(ctx->object),ctx->handleCount };
		}
	};
}
#endif
